package org.qpcr.community.project.service;

import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.hswebframework.ezorm.core.param.Term;
import org.hswebframework.ezorm.rdb.mapping.defaults.SaveResult;
import org.hswebframework.web.api.crud.entity.PagerResult;
import org.hswebframework.web.api.crud.entity.QueryParamEntity;
import org.hswebframework.web.bean.FastBeanCopier;
import org.hswebframework.web.crud.events.EntityDeletedEvent;
import org.hswebframework.web.crud.service.GenericReactiveCrudService;
import org.hswebframework.web.exception.I18nSupportException;
import org.hswebframework.web.exception.NotFoundException;
import org.qpcr.community.device.entity.DeviceInstanceEntity;
import org.qpcr.community.device.enums.SyncState;
import org.qpcr.community.device.service.LocalDeviceInstanceService;
import org.qpcr.community.project.model.entity.RelateProItemSeqInstanceEntity;
import org.qpcr.community.project.model.entity.TestItemInstanceEntity;
import org.qpcr.community.project.model.entity.TestSequenceInstanceEntity;
import org.qpcr.community.project.response.RelateProItemSeqDetail;
import org.qpcr.community.project.response.RelateProItemSeqDetailSync;
import org.reactivestreams.Publisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class LocalRelateProItemSeqInstanceService extends GenericReactiveCrudService<RelateProItemSeqInstanceEntity, String> {
    private final LocalTestItemInstanceService testItemInstanceService;
    private final LocalTestSequenceInstanceService testSequenceInstanceService;
    private final LocalDeviceInstanceService deviceInstanceService;

    //分页查询关联详情列表
    public Mono<PagerResult<RelateProItemSeqDetail>> queryRelateProItemSeqDetail(QueryParamEntity entity, Integer type) {
        Term term = Term.of("type", "eq", type, "");
        entity.getTerms().add(term);
        return this
                .queryPager(entity)
                .filter(e -> CollectionUtils.isNotEmpty(e.getData()))
                .flatMap(result -> this
                        .convertRelateProItemSeqInstanceToDetail(result.getData(), type)
                        .collectList()
                        .map(detailList -> PagerResult.of(result.getTotal(), detailList, entity)))
                .defaultIfEmpty(PagerResult.empty());
    }

    private Flux<RelateProItemSeqDetail> convertRelateProItemSeqInstanceToDetail(List<RelateProItemSeqInstanceEntity> instanceList,
                                                                                 Integer type) {
        if (CollectionUtils.isEmpty(instanceList)) {
            return Flux.empty();
        }
        List<String> idList = instanceList.stream().map(RelateProItemSeqInstanceEntity::getId).collect(Collectors.toList());
        List<String> relateIdList = instanceList.stream().map(RelateProItemSeqInstanceEntity::getRelateId).collect(Collectors.toList());
        QueryParamEntity param = new QueryParamEntity();
        Term term = Term.of("id", "in", relateIdList, "");
        param.getTerms().add(term);
        param.setPaging(false);

        return Mono
                .zip(
                        //T1:查询出所有检测项信息
                        type == 1 ? testItemInstanceService.query(param)
                                .collect(
                                        Collectors.toMap(TestItemInstanceEntity::getId, Function.identity())
                                ) : Mono.just(new HashMap<String, TestItemInstanceEntity>()),
                        //T2:查询出所有序列信息
                        type == 2 ? testSequenceInstanceService.query(param)
                                .collect(
                                        Collectors.toMap(TestSequenceInstanceEntity::getId, Function.identity())
                                ) : Mono.just(new HashMap<String, TestSequenceInstanceEntity>()),
                        //T3:查询测光通道信息
                        type == 1 ? testItemInstanceService.findChannelMapByIds(relateIdList)
                                .defaultIfEmpty(new HashMap<>()) : Mono.just(new HashMap<String, String>())
                )
                .flatMapMany(tp3 -> Flux
                        //遍历关系,将检测项或序列信息转为详情.
                        .fromIterable(instanceList)
                        .flatMap(instance -> {
                                    RelateProItemSeqDetail detail = new RelateProItemSeqDetail();
                                    detail.setId(instance.getId());
                                    detail.setProductId(instance.getProductId());
                                    detail.setRelateId(instance.getRelateId());
                                    detail.setType(instance.getType());
                                    detail.setCreatorId(instance.getCreatorId());
                                    detail.setCreatorName(instance.getCreatorName());
                                    detail.setCreateTime(instance.getCreateTime());
                                    detail.setModifyTime(instance.getModifyTime());
                                    detail.setModifierId(instance.getModifierId());
                                    detail.setModifierName(instance.getModifierName());
                                    TestItemInstanceEntity item = tp3.getT1().get(instance.getRelateId());
                                    TestSequenceInstanceEntity sequence = tp3.getT2().get(instance.getRelateId());
                                    Map<String, String> channelMap = tp3.getT3();

                                    if (type == 1 && item != null) {
                                        detail.setName(item.getItem());
                                        detail.setNameEn(item.getItemEn());
                                        detail.setCode(item.getCode());
                                        // 设置测光通道
                                        detail.setChannel(channelMap.get(instance.getRelateId()));
                                    } else if (type == 2 && sequence != null) {
                                        detail.setName(sequence.getName());
                                        detail.setCode(sequence.getCode());
                                    }
                                    return Mono.just(detail);
                                }
                        ))
                //createRelateProItemSeqDetail是异步操作,可能导致顺序错乱.进行重新排序.
                .sort(Comparator.comparingInt(detail -> idList.indexOf(detail.getId())));
    }

    @Override
    public Mono<SaveResult> save(Publisher<RelateProItemSeqInstanceEntity> entityPublisher) {
        Flux<RelateProItemSeqInstanceEntity> replayedFlux = Flux.from(entityPublisher)
                .replay()
                .autoConnect();
        return super.save(replayedFlux)
                .then(this.updateSyncState(replayedFlux))
                .flatMap(integer -> Mono.just(SaveResult.of(1, integer)));
    }
    @Override
    public Mono<Integer> insert(Publisher<RelateProItemSeqInstanceEntity> entityPublisher) {
        Flux<RelateProItemSeqInstanceEntity> replayedFlux = Flux.from(entityPublisher)
                .replay()
                .autoConnect();
        return super.insert(replayedFlux)
                .then(this.updateSyncState(replayedFlux))
                .flatMap(integer -> Mono.just(SaveResult.of(1, integer).getTotal()));
    }

    @Override
    public Mono<Integer> insertBatch(Publisher<? extends Collection<RelateProItemSeqInstanceEntity>> entityPublisher) {
        return Flux.from(entityPublisher)
                .flatMapIterable(Function.identity())
                .as(this::insert);
    }

    @Override
    public Mono<Integer> updateById(String id, Mono<RelateProItemSeqInstanceEntity> entityPublisher) {
        Flux<RelateProItemSeqInstanceEntity> replayedFlux = Flux.from(entityPublisher)
                .replay()
                .autoConnect();
        return Mono.from(this.findById(id)
                .flatMap(entity -> super.updateById(id, Mono.from(replayedFlux))
                        .then(this.updateSyncState(replayedFlux, entity))
                        .flatMap(integer -> Mono.just(SaveResult.of(0, integer + 1).getTotal()))
                )
        );
    }

    @Override
    public Mono<Integer> deleteById(Publisher<String> idPublisher) {
        Flux<String> replayedFlux = Flux.from(idPublisher)
                .replay()
                .autoConnect();
        return Mono.from(
                this.findById(replayedFlux)
                .flatMap(entity -> super.deleteById(replayedFlux)
                        .then(this.updateSyncState(Mono.just(entity)))
                        .flatMap(integer -> Mono.just(SaveResult.of(1, integer).getTotal()))
                )
        );

    }

    /**
     * 根据类型更新设备检测项、序列同步状态
     *
     * @param entityPublisher RelateProItemSeqInstanceEntity
     * @return Mono<Integer>
     */
    private Mono<Integer> updateSyncState(Publisher<RelateProItemSeqInstanceEntity> entityPublisher) {
        return Mono.from(entityPublisher)
                .flatMap(entity ->
                        entity.getType() == 1 ? deviceInstanceService.updateItemSyncStatusByProductId(Collections.singletonList(entity.getProductId()), SyncState.notSync)
                                : deviceInstanceService.updateSeqSyncStatusByProductId(Collections.singletonList(entity.getProductId()), SyncState.notSync)
                );
    }

    /**
     * 根据类型更新设备检测项、序列同步状态
     *
     * @param entityPublisher RelateProItemSeqInstanceEntity
     * @param entityDb         前端传的type，如果和数据库的不一样，两个状态都要更新
     * @return Mono<Integer>
     */
    private Mono<Integer> updateSyncState(Publisher<RelateProItemSeqInstanceEntity> entityPublisher, RelateProItemSeqInstanceEntity entityDb) {
        return Mono.from(entityPublisher)
                .flatMap(entity -> {
                    if (entityDb.getType() != entity.getType()) {
                        return deviceInstanceService.updateItemSyncStatusByProductId(Collections.singletonList(entityDb.getProductId()), SyncState.notSync)
                                .flatMap(integer -> deviceInstanceService.updateSeqSyncStatusByProductId(Collections.singletonList(entityDb.getProductId()), SyncState.notSync)
                                        .flatMap(integer1 -> Mono.just(integer + integer1))
                                );

                    } else {
                       return entity.getType() == 1 ? deviceInstanceService.updateItemSyncStatusByProductId(Collections.singletonList(entityDb.getProductId()), SyncState.notSync)
                                : deviceInstanceService.updateSeqSyncStatusByProductId(Collections.singletonList(entityDb.getProductId()), SyncState.notSync);
                    }
                });
    }

    /**
     * 查询未同步的设备的检测项和序列信息
     *
     * @param sync 是否同步 否紧查询，是查询后更新状态
     * @return
     */
    @Deprecated
    public Mono<Map<String, Map<String, List<RelateProItemSeqDetail>>>> queryToDevice(boolean sync) {
        // 设备列表
        return deviceInstanceService.queryNotSyncList()
                .flatMap(list -> {
                    // 关系列表
                    return this.createQuery()
                            .where()
                            .in(RelateProItemSeqInstanceEntity::getProductId,
                                    list.stream().map(DeviceInstanceEntity::getProductId).distinct().collect(Collectors.toList())
                            ).fetch()
                            .collectList()
                            .flatMap(relateList -> {
                                // 检测项关系列表
                                List<RelateProItemSeqInstanceEntity> proItemList = relateList.stream()
                                        .filter(entity -> entity.getRelateId()!=null && entity.getType() == 1).collect(Collectors.toList());
                                // 序列关系列表
                                List<RelateProItemSeqInstanceEntity> proSeqList = relateList.stream()
                                        .filter(entity -> entity.getRelateId()!=null && entity.getType() == 2).collect(Collectors.toList());
                                // 更新状态未同步中
                                if (Boolean.TRUE.equals(sync)) {
                                    List<String> itemProductIdList = proItemList.stream().map(RelateProItemSeqInstanceEntity::getProductId).collect(Collectors.toList());
                                    List<String> seqProductIdList = proItemList.stream().map(RelateProItemSeqInstanceEntity::getProductId).collect(Collectors.toList());
                                    if (Boolean.FALSE.equals(itemProductIdList.isEmpty())) {
                                        deviceInstanceService.updateItemSyncStatusByProductId(itemProductIdList, SyncState.inSync);
                                    }
                                    if (Boolean.FALSE.equals(seqProductIdList.isEmpty())) {
                                        deviceInstanceService.updateSeqSyncStatusByProductId(seqProductIdList, SyncState.inSync);
                                    }
                                }
                                return Mono.zip(
                                        // 检测项
                                        proItemList.size() > 0 ? Mono.from(this.convertRelateProItemSeqInstanceToDetail(proItemList, 1)
                                                .collectList()) : Mono.just(new ArrayList<RelateProItemSeqDetail>()),
                                        // 检测序列
                                        proSeqList.size() > 0 ? Mono.from(this.convertRelateProItemSeqInstanceToDetail(proSeqList, 2)
                                                .collectList()) : Mono.just(new ArrayList<RelateProItemSeqDetail>())
                                ).map(tp2 -> {
                                    Map<String, Map<String, List<RelateProItemSeqDetail>>> mapDev = new HashMap<>();
                                    list.forEach(deviceInstance -> {
                                        Map<String, List<RelateProItemSeqDetail>> map = new HashMap<>();
                                        List<RelateProItemSeqDetail> itemList = tp2.getT1()
                                                .stream().filter(relateProItemSeqDetail ->
                                                        deviceInstance.getProductId().equals(relateProItemSeqDetail.getProductId()))
                                                .collect(Collectors.toList());
                                        List<RelateProItemSeqDetail> seqList = tp2.getT2()
                                                .stream().filter(relateProItemSeqDetail ->
                                                        deviceInstance.getProductId().equals(relateProItemSeqDetail.getProductId()))
                                                .collect(Collectors.toList());
                                        map.put("itemList", itemList);
                                        map.put("seqList", seqList);
                                        mapDev.put(deviceInstance.getId(), map);
                                    });
                                    return mapDev;
                                });
                            });
                });
    }

    /**
     * 查询未同步的设备的检测项和序列信息
     *
     * @param sync           是否同步 否紧查询，是查询后更新状态
     * @param devideId       设备id
     * @param lastUpdateTime 上次更新时间
     * @return Mono<Map < String, List < RelateProItemSeqDetailSync>>>
     */
    public Mono<Map<String, List<RelateProItemSeqDetailSync>>> queryToDevice(boolean sync, String devideId, Long lastUpdateTime) {
        // 设备列表
        return deviceInstanceService.findById(devideId)
                .flatMap(deviceInstanceEntity -> {
                    // 关系列表
                    return this.createQuery()
                            .and(RelateProItemSeqInstanceEntity::getProductId, deviceInstanceEntity.getProductId()
                            ).fetch()
                            .collectList()
                            .flatMap(relateList -> {
                                // 检测项关系列表
                                List<RelateProItemSeqInstanceEntity> proItemList = relateList.stream()
                                        .filter(entity -> entity.getRelateId()!=null && entity.getType() == 1).collect(Collectors.toList());
                                // 序列关系列表
                                List<RelateProItemSeqInstanceEntity> proSeqList = relateList.stream()
                                        .filter(entity -> entity.getRelateId()!=null && entity.getType() == 2).collect(Collectors.toList());
                                // 更新状态未同步中
                                if (Boolean.TRUE.equals(sync)) {
                                    List<String> itemProductIdList = proItemList.stream().map(RelateProItemSeqInstanceEntity::getProductId).distinct().collect(Collectors.toList());
                                    List<String> seqProductIdList = proItemList.stream().map(RelateProItemSeqInstanceEntity::getProductId).distinct().collect(Collectors.toList());
                                    if (Boolean.FALSE.equals(itemProductIdList.isEmpty())) {
                                        deviceInstanceService.updateItemSyncStatusByProductId(itemProductIdList, SyncState.inSync);
                                    }
                                    if (Boolean.FALSE.equals(seqProductIdList.isEmpty())) {
                                        deviceInstanceService.updateSeqSyncStatusByProductId(seqProductIdList, SyncState.inSync);
                                    }
                                }
                                return Mono.zip(
                                        // 检测项
                                        proItemList.size() > 0 ? Mono.from(this.convertRelateProItemSeqInstanceToDetailSync(proItemList, 1)
                                                .collectList()) : Mono.just(new ArrayList<RelateProItemSeqDetailSync>()),
                                        // 检测序列
                                        proSeqList.size() > 0 ? Mono.from(this.convertRelateProItemSeqInstanceToDetailSync(proSeqList, 2)
                                                .collectList()) : Mono.just(new ArrayList<RelateProItemSeqDetailSync>())
                                ).map(tp2 -> {
                                    Map<String, List<RelateProItemSeqDetailSync>> map = new HashMap<>();
                                    List<RelateProItemSeqDetailSync> itemList = tp2.getT1()
                                            .stream().filter(relateProItemSeqDetailSync ->
                                                    deviceInstanceEntity.getProductId().equals(relateProItemSeqDetailSync.getProductId())
                                                    && (lastUpdateTime == null || lastUpdateTime != null && lastUpdateTime < relateProItemSeqDetailSync.getLastUpdateTime())
                                            )
                                            .collect(Collectors.toList());
                                    List<RelateProItemSeqDetailSync> seqList = tp2.getT2()
                                            .stream().filter(relateProItemSeqDetailSync ->
                                                    deviceInstanceEntity.getProductId().equals(relateProItemSeqDetailSync.getProductId())
                                                    && (lastUpdateTime == null || lastUpdateTime != null && lastUpdateTime < relateProItemSeqDetailSync.getLastUpdateTime())
                                            )
                                            .collect(Collectors.toList());
                                    map.put("itemList", itemList);
                                    map.put("seqList", seqList);
                                    System.out.println("==================================RelateProItemSeqDetailSync Data" + JSON.toJSONString(map));
                                    return map;
                                });
                            });
                });
    }
    private Flux<RelateProItemSeqDetailSync> convertRelateProItemSeqInstanceToDetailSync(List<RelateProItemSeqInstanceEntity> instanceList,
                                                                                  Integer type) {
        if (CollectionUtils.isEmpty(instanceList)) {
            return Flux.empty();
        }
        List<String> idList = instanceList.stream().map(RelateProItemSeqInstanceEntity::getId).collect(Collectors.toList());
        List<String> relateIdList = instanceList.stream().map(RelateProItemSeqInstanceEntity::getRelateId).collect(Collectors.toList());

        return Mono
                .zip(
                        //T1:查询出所有检测项信息
                        type == 1 ? testItemInstanceService.createQuery()
                                .in(TestItemInstanceEntity::getId, relateIdList)
                                .fetch()
                                .collect(
                                        Collectors.toMap(TestItemInstanceEntity::getId, Function.identity())
                                ) : Mono.just(new HashMap<String, TestItemInstanceEntity>()),
                        //T2:查询出所有序列信息
                        type == 2 ? testSequenceInstanceService.createQuery()
                                .in(TestSequenceInstanceEntity::getId, relateIdList)
                                .fetch()
                                .collect(
                                        Collectors.toMap(TestSequenceInstanceEntity::getId, Function.identity())
                                ) : Mono.just(new HashMap<String, TestSequenceInstanceEntity>())
                )
                .flatMapMany(tp2 -> Flux
                        //遍历关系,将检测项或序列信息转为详情.
                        .fromIterable(instanceList)
                        .flatMap(instance -> {
                                    RelateProItemSeqDetailSync detail = new RelateProItemSeqDetailSync();
                                    // 最新修改时间
                                    detail.setLastUpdateTime(instance.getModifyTime());

                                    TestItemInstanceEntity item = tp2.getT1().get(instance.getRelateId());
                                    TestSequenceInstanceEntity sequence = tp2.getT2().get(instance.getRelateId());

                                    if (type == 1 && item != null) {
                                        detail.setName(item.getItem());
                                        detail.setNameEn(item.getItemEn());
                                        FastBeanCopier.copy(item, detail);
                                        detail.setCode(item.getCode());
                                        // 取关系表和item表最新的时间为最后更新时间
                                        if (instance.getModifyTime() < item.getModifyTime()) {
                                            detail.setLastUpdateTime(item.getModifyTime());
                                        }
                                    } else if (type == 2 && sequence != null) {
                                        FastBeanCopier.copy(sequence, detail);
                                        // 取关系表和序列表最新的时间为最后更新时间
                                        if (instance.getModifyTime() < sequence.getModifyTime()) {
                                            detail.setLastUpdateTime(sequence.getModifyTime());
                                        }
                                    }
                                    detail.setProductId(instance.getProductId());
                                    return Mono.just(detail);
                                }
                        ))
                //createRelateProItemSeqDetail是异步操作,可能导致顺序错乱.进行重新排序.
                .sort(Comparator.comparingInt(detail -> idList.indexOf(detail.getId())));
    }

    /**
     * 查询未同步的设备的检测项和序列信息
     *
     * @return
     */
    public Mono<Map<String, List<RelateProItemSeqDetailSync>>> syncToDevice(String devideId, Long lastUpdateTime) {
        return this.queryToDevice(true, devideId, lastUpdateTime);
    }

    /**
     * 删除检测项关联删除关系
     *
     * @param itemId 检测项id
     * @return Mono<TestItemInstanceEntity>
     */
    public Mono<TestItemInstanceEntity> deleteByItemId(String itemId) {
        return testItemInstanceService.findById(Mono.just(itemId)).switchIfEmpty(Mono.error(NotFoundException.NoStackTrace::new))
                .flatMap((e) -> this.createDelete()
                        .and(RelateProItemSeqInstanceEntity::getRelateId, itemId)
                        .execute()
                        .flatMap(i -> testItemInstanceService.deleteById(Mono.just(itemId))
                                .thenReturn(e)
                        )
                );
    }

    /**
     * 删除序列关联删除关系
     *
     * @param seqId 序列id
     * @return Mono<TestSequenceInstanceEntity>
     */
    public Mono<TestSequenceInstanceEntity> deleteBySeqId(String seqId) {
        return testSequenceInstanceService.findById(Mono.just(seqId)).switchIfEmpty(Mono.error(NotFoundException.NoStackTrace::new))
                .flatMap((e) -> this.createDelete()
                        .and(RelateProItemSeqInstanceEntity::getRelateId, seqId)
                        .execute()
                        .flatMap(i -> testSequenceInstanceService.deleteById(Mono.just(seqId))
                                .thenReturn(e)
                        )
                );
    }

    /**
     * 存在被引用时不可删除
     */
    @EventListener
    public void handleEventItem(EntityDeletedEvent<TestItemInstanceEntity> event) {
        event.async(
                Flux.fromIterable(event.getEntity())
                        .map(TestItemInstanceEntity::getId)
                        .collectList()
                        .filter(CollectionUtils::isNotEmpty)
                        .flatMapMany(ids -> this
                                .createQuery()
                                .in(RelateProItemSeqInstanceEntity::getRelateId, ids)
                                .count()
                                .filter(i -> i <= 0)
                                .switchIfEmpty(Mono.error(() -> new I18nSupportException("该检测项已被设备绑定，请先解除绑定后删除"))))
        );
    }

    /**
     * 存在被引用时不可删除
     */
    @EventListener
    public void handleEventSequence(EntityDeletedEvent<TestSequenceInstanceEntity> event) {
        event.async(
                Flux.fromIterable(event.getEntity())
                        .map(TestSequenceInstanceEntity::getId)
                        .collectList()
                        .filter(CollectionUtils::isNotEmpty)
                        .flatMapMany(ids -> this
                                .createQuery()
                                .in(RelateProItemSeqInstanceEntity::getRelateId, ids)
                                .count()
                                .filter(i -> i <= 0)
                                .switchIfEmpty(Mono.error(() -> new I18nSupportException("该检测序列已被设备绑定，请先解除绑定后删除"))))
        );
    }

}
